package cn.abyss.admin.ums.business;

import cn.abyss.admin.common.util.RequestUtil;
import cn.abyss.admin.config.security.JwtTokenUtil;
import cn.abyss.admin.consts.AdminWebConstants;
import cn.abyss.admin.enums.AdminWebExceptionEnum;
import cn.abyss.admin.enums.BaseAdminWebExceptionLevelEnum;
import cn.abyss.admin.exception.AdminWebException;
import cn.abyss.admin.exception.BaseException;
import cn.abyss.admin.ums.model.*;
import cn.abyss.admin.ums.model.bo.AdminUserDetails;
import cn.abyss.admin.ums.model.dto.MemberRequestDTO;
import cn.abyss.admin.ums.model.param.UmsAdminParam;
import cn.abyss.admin.ums.service.RedisService;
import cn.abyss.admin.ums.service.UmsAdminService;
import cn.abyss.admin.common.exception.Asserts;
import cn.hutool.core.collection.CollUtil;
import io.netty.util.internal.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Description:
 *
 * @author zhangsj
 * @date Created on 2021/8/19
 */
@Component
public class UmsAdminBusiness {

    @Value("${redis.key.prefix.authCode}")
    private String REDIS_KEY_PREFIX_AUTH_CODE;
    @Value("${redis.key.expire.authCode}")
    private Long AUTH_CODE_EXPIRE_SECONDS;
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UmsAdminService umsAdminService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private UmsAdminCacheBusiness umsAdminCacheBusiness;
    @Autowired
    private UmsResourceBusiness umsResourceBusiness;
    @Autowired
    private UmsPermissionBusiness umsPermissionBusiness;
    @Autowired
    private UmsMenuBusiness umsMenuBusiness;
    @Autowired
    private UmsRoleBusiness umsRoleBusiness;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    /**
     * 获取验证码
     *
     * @param requestDTO
     * @return
     */
    public Map<String, Object> getAuthCode(MemberRequestDTO requestDTO) {
        try {
            if (StringUtil.isNullOrEmpty(requestDTO.getTelephone())) {
                throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                        AdminWebExceptionEnum.ADMIN_WEB_MEMBER_NO_PARA, BaseAdminWebExceptionLevelEnum.INTERNAL_EXCEPTION);
            }
            Map<String, Object> map = generateAuthCode(requestDTO.getTelephone());
            return map;
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_MEMBER_AUTHCODE_FAIL, e);
        }
    }

    private Map<String, Object> generateAuthCode(String telephone) {
        Map<String, Object> map = new HashMap<>();
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            sb.append(random.nextInt(10));
        }
        //验证码绑定手机并存储到Redis
        redisService.set(REDIS_KEY_PREFIX_AUTH_CODE + telephone, sb.toString());
        redisService.expire(REDIS_KEY_PREFIX_AUTH_CODE + telephone, AUTH_CODE_EXPIRE_SECONDS);
        map.put("authCode", sb.toString());
        return map;
    }

    /**
     * 判断验证码是否正确
     *
     * @param requestDTO
     * @return
     */
    public Map<String, Object> verifyAuthCode(MemberRequestDTO requestDTO) {
        try {
            if (StringUtil.isNullOrEmpty(requestDTO.getTelephone()) || StringUtil.isNullOrEmpty(requestDTO.getAuthCode())) {
                throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                        AdminWebExceptionEnum.ADMIN_WEB_MEMBER_NO_PARA, BaseAdminWebExceptionLevelEnum.INTERNAL_EXCEPTION);
            }
            Map<String, Object> map = checkAuthCode(requestDTO.getTelephone(), requestDTO.getAuthCode());
            return map;
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_MEMBER_AUTHCODE_FAIL, e);
        }
    }

    private Map<String, Object> checkAuthCode(String telephone, String authCode) {
        Map<String, Object> map = new HashMap<>();
        String authCodeFromRedis = redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone);
        if (StringUtil.isNullOrEmpty(authCodeFromRedis)) {
            map.put("status", "2");
            map.put("msg", "请重新生成手机验证码");
        }
        if (authCode.equals(authCodeFromRedis)) {
            map.put("status", "0");
            map.put("msg", "校验成功");
        } else {
            map.put("status", "1");
            map.put("msg", "校验失败");
        }
        return map;

    }

    /**
     * 注册用户
     *
     * @param umsAdminParam
     * @return
     */
    public void register(UmsAdminParam umsAdminParam) {
        try {
            UmsAdmin umsAdmin = new UmsAdmin();
            BeanUtils.copyProperties(umsAdminParam, umsAdmin);
            umsAdmin.setCreateTime(new Date());
            umsAdmin.setStatus(1l);
            //查询是否有相同用户名的用户
            Map<String, Object> map = new HashMap<>();
            map.put("username", umsAdminParam.getUsername());
            List<UmsAdmin> umsAdminList = umsAdminService.query(map);
            if (CollectionUtils.isEmpty(umsAdminList) || umsAdminList.size() > 0) {
                throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                        AdminWebExceptionEnum.ADMIN_WEB_MEMBER_QUERY_FAIL, BaseAdminWebExceptionLevelEnum.INTERNAL_EXCEPTION);
            }
            //将密码进行加密操作
            String encodePassword = passwordEncoder.encode(umsAdmin.getPassword());
            umsAdmin.setPassword(encodePassword);
            umsAdminService.insert(umsAdmin);
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_MEMBER_REGISTER_FAIL, e);
        }
    }

    /**
     * 登录用户
     */
    public Map<String, Object> login(UmsAdminParam umsAdminParam) {
        String token = null;

        //密码前端需加密（防止明文被截取信息）
        try {
            if (StringUtil.isNullOrEmpty(umsAdminParam.getUsername())
                    || StringUtil.isNullOrEmpty(umsAdminParam.getPassword())) {
                throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                        AdminWebExceptionEnum.ADMIN_WEB_MEMBER_NO_PARA, BaseAdminWebExceptionLevelEnum.INTERNAL_EXCEPTION);
            }
            //获取用户信息
            UserDetails userDetails = loadUserDetailsByUsername(umsAdminParam.getUsername());
            if (!passwordEncoder.matches(umsAdminParam.getPassword(), userDetails.getPassword())) {
                Asserts.fail("密码不正确");
            }
            if (!userDetails.isEnabled()) {
                Asserts.fail("帐号已被禁用");
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
//            updateLoginTimeByUsername(username);
            insertLoginLog(umsAdminParam.getUsername());
            Map<String, Object> map = new HashMap<>();
            map.put("token", token);
            map.put("tokenHead", tokenHead);
            return map;
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_MEMBER_LOGIN_FAIL, e);
        }
    }

    /**
     * 添加登录记录
     *
     * @param username 用户名
     */
    private void insertLoginLog(String username) {
        UmsAdmin admin = getAdminByUsername(username);
        if (admin == null) return;
        UmsAdminLoginLog loginLog = new UmsAdminLoginLog();
        loginLog.setAdminId(admin.getId());
        loginLog.setCreateTime(new Date());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        loginLog.setIp(RequestUtil.getRequestIp(request));
        umsAdminService.insertLoginLog(loginLog);
    }

    /**
     * 私有方法-获取用户信息
     *
     * @param username
     * @return
     */
    public UserDetails loadUserDetailsByUsername(String username) {
        Map<String, Object> map = new HashMap<>();
        //得到用户信息,并放入Redis中
        UmsAdmin admin = getAdminByUsername(username);
        if (admin != null) {
            //得到资源信息,并放入Redis中
            map.put("adminId", admin.getId());
            List<UmsPermission> umsPermissionList = umsPermissionBusiness.getPermissionList(map);
            //构建用户详情
            return new AdminUserDetails(admin, umsPermissionList);
        }
        throw new UsernameNotFoundException("用户名或密码错误");
    }

    /**
     * 得到资源信息,并放入Redis中
     *
     * @param adminId
     * @return
     */
    public List<UmsResource> getResourceList(Long adminId) {
        try {
            List<UmsResource> resourceList = umsAdminCacheBusiness.getResourceList(adminId);
            if (CollectionUtils.isEmpty(resourceList)) {
                resourceList = umsResourceBusiness.getResourceList(adminId);
                if (!CollectionUtils.isEmpty(resourceList)) {
                    umsAdminCacheBusiness.setResourceList(adminId, resourceList);
                }
            }
            return resourceList;
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_RESOURCE_QUERY_BYNAME_FAIL, e);
        }
    }

    /**
     * 得到用户信息,并放入Redis中
     *
     * @param username
     * @return
     */
    public UmsAdmin getAdminByUsername(String username) {
        try {
            UmsAdmin admin = umsAdminCacheBusiness.getAdmin(username);
            if (admin != null) return admin;
            Map<String, Object> map = new HashMap<>();
            map.put("username", username);
            List<UmsAdmin> adminList = umsAdminService.query(map);
            if (CollectionUtils.isEmpty(adminList) || adminList.size() > 1) {
                throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                        AdminWebExceptionEnum.ADMIN_WEB_MEMBER_QUERY_FAIL, BaseAdminWebExceptionLevelEnum.INTERNAL_EXCEPTION);
            }
            admin = adminList.get(0);
            umsAdminCacheBusiness.setAdmin(admin);
            return admin;
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_MEMBER_QUERY_BYNAME_FAIL, e);
        }
    }


    public UmsAdmin getAdminById(Long adminId) {
        try {
            UmsAdmin umsAdmin = umsAdminService.getById(adminId);
            return umsAdmin;
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_MEMBER_QUERY_BYID_FAIL, e);
        }
    }

    public List<UmsPermission> getPermissionList(Long adminId) {
        try {
            Map<String, Object> map = new HashMap<>();
            map.put("adminId", adminId);
            List<UmsPermission> umsPermissionList = umsPermissionBusiness.getPermissionList(map);
            return umsPermissionList;
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_PERMISSION_QUERY_BYNAME_FAIL, e);
        }
    }

    /**
     * 查询登录信息
     *
     * @param principal
     * @return
     */
    public Map<String, Object> getAdminInfo(Principal principal) {
        try {
            if (principal == null) {
                throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                        AdminWebExceptionEnum.ADMIN_WEB_MEMBER_NO_PARA, BaseAdminWebExceptionLevelEnum.INTERNAL_EXCEPTION);
            }
            String username = principal.getName();
            Map<String, Object> map = new HashMap<>();
            map.put("username", principal.getName());
            List<UmsAdmin> umsAdmins = umsAdminService.query(map);
            if (CollectionUtils.isEmpty(umsAdmins) || umsAdmins.size()>1) {
                throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                        AdminWebExceptionEnum.ADMIN_WEB_MEMBER_QUERY_BYNAME_FAIL, BaseAdminWebExceptionLevelEnum.INTERNAL_EXCEPTION);
            }
            UmsAdmin umsAdmin = umsAdmins.get(0);
            Map<String, Object> data = new HashMap<>();
            data.put("username", umsAdmin.getUsername());
            data.put("menus", umsMenuBusiness.getMenuList(umsAdmin.getId()));
            data.put("icon", umsAdmin.getIcon());
            List<UmsRole> roleList = umsRoleBusiness.getRoleList(umsAdmin.getId());
            if (CollUtil.isNotEmpty(roleList)) {
                List<String> roles = roleList.stream().map(UmsRole::getName).collect(Collectors.toList());
                data.put("roles", roles);
            }
            return data;
        } catch (BaseException bex) {
            throw bex;
        } catch (Exception e) {
            throw new AdminWebException(AdminWebConstants.UMS_ADMIN,
                    AdminWebExceptionEnum.ADMIN_WEB_PERMISSION_QUERY_BYNAME_FAIL, e);
        }
    }
}
